package ocllibrary;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import project.Project;
import uml2.kernel.Operation;
import uml2.kernel.Parameter;
import uml2.kernel.ParameterDirectionKindEnum;
import uml2.kernel.Type;
import uml2.kernel.VisibilityKindEnum;
import exceptions.ConstraintException;
import facade.UserFacade;
import facade.kernel.DataTypeFacade;
import facade.kernel.OperationFacade;
import facade.kernel.ParameterFacade;

public class OclLibrary {
	Project p;
	DataTypeFacade dtf;
	OperationFacade of;
	UserFacade uf;
	
	LinkedList opnames = new LinkedList();
	
	private Map operationsByName = new HashMap();
	
	private abstract static class OpsCreator {
        public abstract Object creator();
    }
	
	public OclLibrary(Project p) {
		this.p = p;
		this.dtf = new DataTypeFacade(p);
		this.of = new OperationFacade(p);
		this.uf = new UserFacade(p);
		
		fillOpsListNames();
		initialize();
	}
	
	private void fillOpsListNames() {
		this.opnames = new LinkedList();
		this.opnames.add("=");
		this.opnames.add("<>");
		this.opnames.add(">");
		this.opnames.add("<");
		this.opnames.add("<=");
		this.opnames.add(">=");
		this.opnames.add("and");
		this.opnames.add("or");
		this.opnames.add("xor");
		this.opnames.add("not");
		this.opnames.add("implies");
		this.opnames.add("+");
		this.opnames.add("-");
		this.opnames.add("*");
		this.opnames.add("/");
		
		this.opnames.add("size");
		this.opnames.add("isEmpty");
		this.opnames.add("notEmpty");
		this.opnames.add("includes");
		this.opnames.add("excludes");
		this.opnames.add("count");
		this.opnames.add("includesAll");
		this.opnames.add("excludesAll");
		this.opnames.add("sum");
		this.opnames.add("product");
		this.opnames.add("union");
		this.opnames.add("intersection");
		this.opnames.add("including");
		this.opnames.add("excluding");
		this.opnames.add("symmetricDifference");
		this.opnames.add("flatten");
		this.opnames.add("asSet");
		this.opnames.add("asOrderedSet");
		this.opnames.add("asSequence");
		this.opnames.add("asBag");
		this.opnames.add("append");
		this.opnames.add("insertAt");
		this.opnames.add("subOrderedSet");
		this.opnames.add("subSequence");
		this.opnames.add("at");
		this.opnames.add("indexOf");
		this.opnames.add("first");
		this.opnames.add("last");
		this.opnames.add("ñ");
		
		this.opnames.add("oclAsType");
		this.opnames.add("oclIsTypeOf");
		this.opnames.add("oclIsKindOf");	
	}
	
	private void initialize() {
		Map c = operationsByName;
		
		c.put("=", 		new OpsCreator() { public Object creator() { return createEqual(); } } );
		c.put("<>", 	new OpsCreator() { public Object creator() { return createDistinct(); } } );
		c.put(">", 		new OpsCreator() { public Object creator() { return createGreaterThan(); } } );
		c.put("<", 		new OpsCreator() { public Object creator() { return createLessThan(); } } );
		c.put(">=", 	new OpsCreator() { public Object creator() { return createGreaterOrEqualThan(); } } );
		c.put("<=", 	new OpsCreator() { public Object creator() { return createLessOrEqualThan(); } } );
		c.put("and", 	new OpsCreator() { public Object creator() { return createAnd(); } } );
		c.put("or", 	new OpsCreator() { public Object creator() { return createOr(); } } );
		c.put("xor", 	new OpsCreator() { public Object creator() { return createXor(); } } );
		c.put("not", 	new OpsCreator() { public Object creator() { return createNot(); } } );
		c.put("implies",new OpsCreator() { public Object creator() { return createImplies(); } } );
		
		c.put("+", 		new OpsCreator() { public Object creator() { return createPlus(); } } );
		c.put("-", 		new OpsCreator() { public Object creator() { return createMinus(); } } );
		c.put("*", 		new OpsCreator() { public Object creator() { return createMultiply(); } } );
		c.put("/", 		new OpsCreator() { public Object creator() { return createDivide(); } } );
		
		c.put("abs", 		new OpsCreator() { public Object creator() { return createAbs(); } } );
		c.put("floor", 		new OpsCreator() { public Object creator() { return createFloor(); } } );
		c.put("round", 		new OpsCreator() { public Object creator() { return createRound(); } } );
		c.put("max", 		new OpsCreator() { public Object creator() { return createMax(); } } );
		c.put("min", 		new OpsCreator() { public Object creator() { return createMin(); } } );
		c.put("div", 		new OpsCreator() { public Object creator() { return createDiv(); } } );
		c.put("mod", 		new OpsCreator() { public Object creator() { return createMod(); } } );
		//c.put("size", 		new OpsCreator() { public Object creator() { return createSize(); } } );
		c.put("concat", 		new OpsCreator() { public Object creator() { return createConcat(); } } );
		c.put("substring", 		new OpsCreator() { public Object creator() { return createSubstring(); } } );
		c.put("toInteger", 		new OpsCreator() { public Object creator() { return createToInteger(); } } );
		c.put("toReal", 		new OpsCreator() { public Object creator() { return createToReal(); } } );
		
		c.put("size", 					new OpsCreator() { public Object creator() { return createSize(); } } );
		c.put("isEmpty",				new OpsCreator() { public Object creator() { return createIsEmpty(); } } );
		c.put("notEmpty",				new OpsCreator() { public Object creator() { return createNotEmpty(); } } );
		c.put("includes", 				new OpsCreator() { public Object creator() { return createIncludes(); } } );
		c.put("excludes", 				new OpsCreator() { public Object creator() { return createExcludes(); } } );
		c.put("count", 					new OpsCreator() { public Object creator() { return createCount(); } } );
		c.put("includesAll",			new OpsCreator() { public Object creator() { return createIncludesAll(); } } );
		c.put("excludesAll",			new OpsCreator() { public Object creator() { return createExcludesAll(); } } );
		c.put("sum", 					new OpsCreator() { public Object creator() { return createSum(); } } );
		c.put("product", 				new OpsCreator() { public Object creator() { return createProduct(); } } );
		c.put("union", 					new OpsCreator() { public Object creator() { return createUnion(); } } );
		c.put("intersection", 			new OpsCreator() { public Object creator() { return createIntersection(); } } );
		c.put("including", 				new OpsCreator() { public Object creator() { return createIncluding(); } } );
		c.put("excluding", 				new OpsCreator() { public Object creator() { return createExcluding(); } } );
		c.put("symmetricDifference", 	new OpsCreator() { public Object creator() { return createSymmetricDifference(); } } );
		c.put("flatten", 				new OpsCreator() { public Object creator() { return createFlatten(); } } );
		c.put("asSet", 					new OpsCreator() { public Object creator() { return createAsSet(); } } );
		c.put("asOrderedSet", 			new OpsCreator() { public Object creator() { return createAsOrderedSet(); } } );
		c.put("asSequence", 			new OpsCreator() { public Object creator() { return createAsSequence(); } } );
		c.put("asBag", 					new OpsCreator() { public Object creator() { return createAsBag(); } } );
		c.put("append", 				new OpsCreator() { public Object creator() { return createAppend(); } } );
		c.put("prepend", 				new OpsCreator() { public Object creator() { return createPrepend(); } } );
		c.put("insertAt", 				new OpsCreator() { public Object creator() { return createInsertAt(); } } );
		c.put("subOrderedSet", 			new OpsCreator() { public Object creator() { return createSubOrderedSet(); } } );
		c.put("subSequence", 			new OpsCreator() { public Object creator() { return createSubSequence(); } } );
		c.put("at", 					new OpsCreator() { public Object creator() { return createAt(); } } );
		c.put("indexOf", 				new OpsCreator() { public Object creator() { return createIndexOf(); } } );
		c.put("first", 					new OpsCreator() { public Object creator() { return createFirst(); } } );
		c.put("last", 					new OpsCreator() { public Object creator() { return createLast(); } } );
		c.put("ñ",	 					new OpsCreator() { public Object creator() { return createNotIn(); } } );
		
		c.put("oclAsType", 	new OpsCreator() { public Object creator() { return createOclAsType(); } } );
		c.put("oclIsTypeOf",new OpsCreator() { public Object creator() { return createOclIsTypeOf(); } } );
		c.put("oclIsKindOf",new OpsCreator() { public Object creator() { return createOclIsKindOf(); } } );
		c.put("oclIsNew",	new OpsCreator() { public Object creator() { return createOclIsNew(); } } );
		
		c.put("@pre",		new OpsCreator() { public Object creator() { return createAtPre(); } } );
	}
	
	public void setTypes() {
		// Create the basic types
		setStringType();
		setBooleanType();
		setIntegerType();
		setRealType();
	}
	
	public boolean setStringType() {
		if (uf.findDataTypeByName("String")!=null) return false;
		else {
			dtf.createDataType("String", VisibilityKindEnum.PUBLIC, false, false);
			return true;
		}
	}
	public boolean setBooleanType() {
		if (uf.findDataTypeByName("Boolean")!=null) return false;
		else {
			dtf.createDataType("Boolean", VisibilityKindEnum.PUBLIC, false, false);
			return true;
		}
	}
	public boolean setRealType() {
		if (uf.findDataTypeByName("Real")!=null) return false;
		else {
			dtf.createDataType("Real", VisibilityKindEnum.PUBLIC, false, false);
			return true;
		}
	}
	public boolean setIntegerType() {
		if (uf.findDataTypeByName("Integer")!=null) return false;
		else {
			dtf.createDataType("Integer", VisibilityKindEnum.PUBLIC, false, false);
			return true;
		}
	}
	
	// Integer and Real Operations
	public Operation createAbs() {
		// abs
		Operation oper = uf.findOperationByName("abs");
		if (oper == null) { 
			oper = of.createOperation("abs", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createFloor() {
		// floor
		Operation oper = uf.findOperationByName("floor");
		if (oper == null) { 
			oper = of.createOperation("floor", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createRound() {
		// round
		Operation oper = uf.findOperationByName("round");
		if (oper == null) { 
			oper = of.createOperation("round", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createMax() {
		// max
		Operation oper = uf.findOperationByName("max");
		if (oper == null) { 
			oper = of.createOperation("max", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createMin() {
		// min
		Operation oper = uf.findOperationByName("min");
		if (oper == null) { 
			oper = of.createOperation("min", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createDiv() {
		// div
		Operation oper = uf.findOperationByName("div");
		if (oper == null) { 
			oper = of.createOperation("div", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createMod() {
		// mod
		Operation oper = uf.findOperationByName("mod");
		if (oper == null) { 
			oper = of.createOperation("mod", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	
	// String Operations
	// public Operation createSize() {} // same as in collection operations
	public Operation createConcat() {
		// concat
		Operation oper = uf.findOperationByName("concat");
		if (oper == null) { 
			oper = of.createOperation("concat", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("String"));
		}
		return oper;
	}
	public Operation createSubstring() {
		// substring
		Operation oper = uf.findOperationByName("substring");
		if (oper == null) { 
			oper = of.createOperation("substring", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("String"));
		}
		return oper;
	}
	public Operation createToInteger() {
		// toInteger
		Operation oper = uf.findOperationByName("toInteger");
		if (oper == null) { 
			oper = of.createOperation("toInteger", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createToReal() {
		// toReal
		Operation oper = uf.findOperationByName("toReal");
		if (oper == null) { 
			oper = of.createOperation("toReal", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Real"));
		}
		return oper;
	}
	
	// Logical Operations
	public Operation createEqual() {
		// equal
		Operation oper = uf.findOperationByName("=");
		if (oper == null) { 
			oper = of.createOperation("=", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createDistinct() {
		// distinct
		Operation oper = uf.findOperationByName("<>");
		if (oper == null) {
			oper = of.createOperation("<>", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createGreaterThan() {
		// greater than
		Operation oper = uf.findOperationByName(">");
		if (oper == null) {
			oper = of.createOperation(">", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createLessThan() {
		Operation oper = uf.findOperationByName("<");
		// less than
		if (oper == null) {
			oper = of.createOperation("<", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createGreaterOrEqualThan() {
		Operation oper = uf.findOperationByName(">=");
		// greater or equal than
		if (oper == null) {
			oper = of.createOperation(">=", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createLessOrEqualThan() {
		Operation oper = uf.findOperationByName("<=");
		// less or equal than
		if (oper == null) {
			oper = of.createOperation("<=", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createAnd() {
		Operation oper = uf.findOperationByName("and");
		// and
		if (oper == null) {
			oper = of.createOperation("and", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createOr() {
		Operation oper = uf.findOperationByName("or");
		// or
		if (oper == null) {
			oper = of.createOperation("or", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createXor() {
		Operation oper = uf.findOperationByName("xor");
		// xor
		if (oper == null) {
			oper = of.createOperation("xor", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createNot() {
		Operation oper = uf.findOperationByName("not");
		// not
		if (oper == null) {
			oper = of.createOperation("not", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createImplies() {
		Operation oper = uf.findOperationByName("implies");
		// implies
		if (oper == null) {
			oper = of.createOperation("implies", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	
	// Aritmetical Operations
	public Operation createPlus() {
		Operation oper = uf.findOperationByName("+");
		// plus
		if (oper == null) {
			oper = of.createOperation("+", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createMinus() {
		Operation oper = uf.findOperationByName("-");
		// minus
		if (oper == null) {
			oper = of.createOperation("-", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createMultiply() {
		Operation oper = uf.findOperationByName("*");
		// multiply
		if (oper == null) {
			oper = of.createOperation("*", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createDivide() {
		Operation oper = uf.findOperationByName("/");
		// divide
		if (oper == null) {
			oper = of.createOperation("/", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	
	// CollectionType's Operations
	public Operation createSize() {
		Operation oper = uf.findOperationByName("size");
		// size
		if (oper == null) {
			oper = of.createOperation("size", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Integer"));
		}
		return oper;
	}
	public Operation createIsEmpty() {
		Operation oper = uf.findOperationByName("isEmpty");
		// isEmpty
		if (oper == null) {
			oper = of.createOperation("isEmpty", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createNotEmpty() {
		Operation oper = uf.findOperationByName("notEmpty");
		// notEmpty
		if (oper == null) {
			oper = of.createOperation("notEmpty", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createIncludes() {
		Operation oper = uf.findOperationByName("includes");
		// includes
		if (oper == null) {
			oper = of.createOperation("includes", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createExcludes() {
		Operation oper = uf.findOperationByName("excludes");
		// excludes
		if (oper == null) {
			oper = of.createOperation("excludes", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createCount() {
		Operation oper = uf.findOperationByName("count");
		// count
		if (oper == null) {
			oper = of.createOperation("count", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createIncludesAll() {
		Operation oper = uf.findOperationByName("includesAll");
		// includesAll
		if (oper == null) {
			oper = of.createOperation("includesAll", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createExcludesAll() {
		Operation oper = uf.findOperationByName("excludesAll");
		// excludesAll
		if (oper == null) {
			oper = of.createOperation("excludesAll", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createSum() {
		Operation oper = uf.findOperationByName("sum");
		// sum
		if (oper == null) {
			oper = of.createOperation("sum", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createProduct() {
		Operation oper = uf.findOperationByName("product");
		// product
		if (oper == null) {
			oper = of.createOperation("product", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createUnion() {
		Operation oper = uf.findOperationByName("union");
		// union
		if (oper == null) {
			oper = of.createOperation("union", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createIntersection() {
		Operation oper = uf.findOperationByName("intersection");
		// intersection
		if (oper == null) {
			oper = of.createOperation("intersection", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createIncluding() {
		Operation oper = uf.findOperationByName("including");
		// including
		if (oper == null) {
			oper = of.createOperation("including", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createExcluding() {
		Operation oper = uf.findOperationByName("excluding");
		// excluding
		if (oper == null) {
			oper = of.createOperation("excluding", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createSymmetricDifference() {
		Operation oper = uf.findOperationByName("symmetricDifference");
		// symmetricDifference
		if (oper == null) {
			oper = of.createOperation("symmetricDifference", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createFlatten() {
		Operation oper = uf.findOperationByName("flatten");
		// flatten
		if (oper == null) {
			oper = of.createOperation("flatten", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAsSet() {
		Operation oper = uf.findOperationByName("asSet");
		// asSet
		if (oper == null) {
			oper = of.createOperation("asSet", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAsOrderedSet() {
		Operation oper = uf.findOperationByName("asOrderedSet");
		// asOrderedSet
		if (oper == null) {
			oper = of.createOperation("asOrderedSet", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAsSequence() {
		Operation oper = uf.findOperationByName("asSequence");
		// asSequence
		if (oper == null) {
			oper = of.createOperation("asSequence", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAsBag() {
		Operation oper = uf.findOperationByName("asBag");
		// asBag
		if (oper == null) {
			oper = of.createOperation("asBag", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAppend() {
		Operation oper = uf.findOperationByName("append");
		// append
		if (oper == null) {
			oper = of.createOperation("append", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createPrepend() {
		Operation oper = uf.findOperationByName("prepend");
		// prepend
		if (oper == null) {
			oper = of.createOperation("prepend", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createInsertAt() {
		Operation oper = uf.findOperationByName("insertAt");
		// insertAt
		if (oper == null) {
			oper = of.createOperation("insertAt", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createSubOrderedSet() {
		Operation oper = uf.findOperationByName("subOrderedSet");
		// subOrderedSet
		if (oper == null) {
			oper = of.createOperation("subOrderedSet", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createSubSequence() {
		Operation oper = uf.findOperationByName("subSequence");
		// subSequence
		if (oper == null) {
			oper = of.createOperation("subSequence", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createAt() {
		Operation oper = uf.findOperationByName("at");
		// at
		if (oper == null) {
			oper = of.createOperation("at", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createIndexOf() {
		Operation oper = uf.findOperationByName("indexOf");
		// indexOf
		if (oper == null) {
			oper = of.createOperation("indexOf", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createFirst() {
		Operation oper = uf.findOperationByName("first");
		// first
		if (oper == null) {
			oper = of.createOperation("first", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createLast() {
		Operation oper = uf.findOperationByName("last");
		// last
		if (oper == null) {
			oper = of.createOperation("last", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createNotIn() {
		Operation oper = uf.findOperationByName("ñ");
		// notIn
		if (oper == null) {
			oper = of.createOperation("ñ", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	
	// Other Operations
	public Operation createOclAsType() {
		Operation oper = uf.findOperationByName("oclAsType");
		// oclAsType
		if (oper == null) {
			oper = of.createOperation("oclAsType", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	public Operation createOclIsTypeOf() {
		Operation oper = uf.findOperationByName("oclIsTypeOf");
		// oclIsTypeOf
		if (oper == null) {
			oper = of.createOperation("oclIsTypeOf", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper,uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createOclIsKindOf() {
		Operation oper = uf.findOperationByName("oclIsKindOf");
		// oclIsKindOf
		if (oper == null) {
			oper = of.createOperation("oclIsKindOf", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper, uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createOclIsNew() {
		Operation oper = uf.findOperationByName("oclIsNew");
		// oclIsNew
		if (oper == null) {
			oper = of.createOperation("oclIsNew", VisibilityKindEnum.PUBLIC, false, false, false);
			setTypeOfBasicOperation(oper, uf.findDataTypeByName("Boolean"));
		}
		return oper;
	}
	public Operation createAtPre() {
		Operation oper = uf.findOperationByName("@pre");
		// oclIsNew
		if (oper == null) {
			oper = of.createOperation("@pre", VisibilityKindEnum.PUBLIC, false, false, false);
		}
		return oper;
	}
	
	// Main method in order to find operations
	public Operation findOclOperationByName(String opname) {
		Operation op = uf.findOperationByName(opname);
		if (op == null) {
			OpsCreator cr = (OpsCreator) operationsByName.get(opname);
			if ( cr == null ) {
				throw new RuntimeException("No creator for type '" + opname + "' in OclLibrary " + this.getClass().getName());
			}
			op = (Operation) cr.creator();
		}
		return op; 
	}
	
	// Other auxiliar methods
	public boolean isAnEspecificatedOperation(String name) {
		if (this.opnames.contains(name)) return true;
		else return false;
	}
	
	public boolean setTypeOfBasicOperation(Operation o, Type t) {
		ParameterFacade pf = new ParameterFacade(p);
		Parameter p = pf.createParameter(false, false, "defaultBasicOperationParameter", null, ParameterDirectionKindEnum.RETURN);
		p.setType(t);
		try {
			p.setOperation(o);
			return true;
		} catch (ConstraintException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public boolean setInParameter(Operation o, Type t) {
		ParameterFacade pf = new ParameterFacade(p);
		Parameter p = pf.createParameter(false, false, "defaultBasicOperationParameter", null, ParameterDirectionKindEnum.IN);
		p.setType(t);
		try {
			p.setOperation(o);
			return true;
		} catch (ConstraintException e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public Type getTypeOfBasicOperation(Operation o) {
		List params = o.getOwnedParameter();
		Iterator it = params.iterator();
		while (it.hasNext()) {
			Parameter p = (Parameter) it.next();
			if (p.getDirection().equals(ParameterDirectionKindEnum.RETURN)) return p.getType(); 
		}
		return null;
	}
}
